﻿using HalconDotNet;
using Kimd.Vision;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace Kimd.Motion
{
    public class TrayHelper : IEnumerable<Cell>
    {
        private Tray _tray;
        private PointF[,] _points;
        private HTuple _mat2D;
        private PointF _start;
        private PointF _end;
        private Cell[,] _cells;
        private bool isSimulate = true;
        public TrayHelper(Tray tray)
        {
            this._tray = tray;
            this._cells = new Cell[_tray.RowCount, _tray.ColCount];
            for (int i = 0; i < _tray.RowCount; i++)
            {
                for (int j = 0; j < _tray.ColCount; j++)
                {
                    this._cells[i, j] = new Cell();
                }
            }
        }
        private bool _isEmpty;
        public bool IsEmpty
        {
            get
            {
                _isEmpty = true;
                for (int i = 0; i < _tray.RowCount; i++)
                {
                    for (int j = 0; j < _tray.ColCount; j++)
                    {
                        if (this._cells[i, j].Status != CellStatus.NoProduct)
                        {
                            _isEmpty = false;
                            break;
                        }
                    }
                    if (!_isEmpty)
                        break;
                }
                return _isEmpty;
            }
        }
        private bool _isFull;
        public bool IsFull
        {
            get
            {
                _isFull = true;
                for (int i = 0; i < _tray.RowCount; i++)
                {
                    for (int j = 0; j < _tray.ColCount; j++)
                    {
                        if (this._cells[i, j].Status == CellStatus.NoProduct)
                        {
                            _isFull = false;
                            break;
                        }
                    }
                    if (!_isFull)
                        break;
                }
                return _isFull;
            }
        }
        public void _putOKProductTray0()
        {
            _cells = new Cell[_tray.RowCount, _tray.ColCount];
            for (int i = 0; i < _tray.RowCount; i++)
            {
                for (int j = 0; j < _tray.ColCount; j++)
                {
                    _cells[i, j] = new Cell() { Status = CellStatus.HasProduct };
                }
            }
        }
        public void NewEmptyTray()
        {
            _cells = new Cell[_tray.RowCount, _tray.ColCount];
            for (int i = 0; i < _tray.RowCount; i++)
            {
                for(int j = 0; j < _tray.ColCount; j++)
                {
                    _cells[i, j] = new Cell();
                }
            }
            for (int i = 0; i < _tray.RowCount * _tray.ColCount; i++)
            {
                SetCellStatus(i, CellStatus.NoProduct);
            }
        }
        public void NewFullTray()
        {
            _cells = new Cell[_tray.RowCount, _tray.ColCount];
            for (int i = 0; i < _tray.RowCount; i++)
            {
                for (int j = 0; j < _tray.ColCount; j++)
                {
                    _cells[i, j] = new Cell();
                }
            }
            for (int i = 0; i < _tray.RowCount * _tray.ColCount; i++)
            {
                SetCellStatus(i, CellStatus.HasProduct);
            }
        }

        public bool Calibrate()
        {
            if (_tray.IsLeftUpSet && _tray.IsLeftDownSet && _tray.IsRightDownSet && _tray.IsRightUpSet)
            {
                try
                {
                    PointF[] pixelPoints = new PointF[4] { new PointF(0, 0), new PointF(0, _tray.ColCount - 1), new PointF(_tray.RowCount - 1, 0), new PointF(_tray.RowCount - 1, _tray.ColCount - 1) };
                    PointF[] worldPoints = new PointF[4] { _tray.LeftUpPoint, _tray.RightUpPoint, _tray.LeftDownPoint, _tray.RightDownPoint };
                    HTuple pixelCrdX, pixelCrdY, worldCrdX, worldCrdY;
                    pixelCrdX = new HTuple();
                    pixelCrdY = new HTuple();
                    worldCrdX = new HTuple();
                    worldCrdY = new HTuple();
                    for (int i = 0; i < pixelPoints.Length; i++)
                    {
                        HOperatorSet.TupleConcat(pixelCrdX, pixelPoints[i].X, out pixelCrdX);
                        HOperatorSet.TupleConcat(pixelCrdY, pixelPoints[i].Y, out pixelCrdY);
                    }
                    for (int i = 0; i < worldPoints.Length; i++)
                    {
                        HOperatorSet.TupleConcat(worldCrdX, worldPoints[i].X, out worldCrdX);
                        HOperatorSet.TupleConcat(worldCrdY, worldPoints[i].Y, out worldCrdY);
                    }
                    HOperatorSet.VectorToHomMat2d(pixelCrdX, pixelCrdY, worldCrdX, worldCrdY, out _mat2D);
                }
                catch
                {
                    return false;
                }
                return true;
            }
            if ((_tray.RowCount == 1 || _tray.ColCount == 1) && _tray.IsLeftUpSet && _tray.IsRightDownSet)
            {
                _start = _tray.LeftUpPoint;
                _end = _tray.RightDownPoint;
                return true;
            }
            return false;
        }
        public PointF GetPoint(int row, int col, int nozzleIndex = 0)
        {
            if (_tray.RowCount == 1 || _tray.ColCount == 1)
            {
                double offsetX = _end.X - _start.X;
                double offsetY = _end.Y - _start.Y;
                double smallOffsetX = 0;
                double smallOffsetY = 0;
                if (_tray.RowCount == 1)
                {
                    smallOffsetX = offsetX / (_tray.ColCount - 1);
                    smallOffsetY = offsetY / (_tray.ColCount - 1);
                    return new PointF((float)(_start.X + smallOffsetX * col), (float)(_start.Y + smallOffsetY * col));
                }
                else
                {
                    smallOffsetX = offsetX / (_tray.RowCount - 1);
                    smallOffsetY = offsetY / (_tray.RowCount - 1);
                    return new PointF((float)(_start.X + smallOffsetX * row), (float)(_start.Y + smallOffsetY * row));
                }
            }
            else
            {
                HTuple wx = new HTuple();
                HTuple wy = new HTuple();
                HOperatorSet.AffineTransPoint2d(_mat2D, new HTuple(row), new HTuple(col), out wx, out wy);
                if (nozzleIndex != 0)
                    return new PointF((float)(wx.D + nozzleIndex * _tray.OffsetX), (float)(wy.D + nozzleIndex * _tray.OffsetY));
                else
                    return new PointF((float)wx.D, (float)wy.D);
            }
        }
        public PointF GetPoint(int index, int nozzleIndex = 0)
        {
            int row = 0;
            int col = 0;
            switch (_tray.Direction)
            {
                case TrayDir.XZ:
                    row = index / _tray.ColCount;
                    col = index % _tray.ColCount;
                    break;
                case TrayDir.XS:
                    row = index / _tray.ColCount;
                    if (row % 2 == 0)
                        col = index % _tray.ColCount;
                    else
                        col = _tray.ColCount - index % _tray.ColCount - 1;
                    break;
                case TrayDir.YZ:
                    col = index / _tray.RowCount;
                    row = index % _tray.RowCount;
                    break;
                case TrayDir.YS:
                    col = index / _tray.RowCount;
                    if (col % 2 == 0)
                        row = index % _tray.RowCount;
                    else
                        row = _tray.ColCount - index % _tray.RowCount - 1;
                    break;
            }
            return GetPoint(row, col, nozzleIndex);
        }
        public double GetZ()
        {
            return _tray.NozzleZ;
        }
        public double GetR()
        {
            return _tray.NozzleR;
        }
        public Point GetCellRowCol(int index)
        {
            int row = 0;
            int col = 0;
            switch (_tray.Direction)
            {
                case TrayDir.XZ:
                    row = index / _tray.ColCount;
                    col = index % _tray.ColCount;
                    break;
                case TrayDir.XS:
                    row = index / _tray.ColCount;
                    if (row % 2 == 0)
                        col = index % _tray.ColCount;
                    else
                        col = _tray.ColCount - index % _tray.ColCount - 1;
                    break;
                case TrayDir.YZ:
                    col = index / _tray.RowCount;
                    row = index % _tray.RowCount;
                    break;
                case TrayDir.YS:
                    col = index / _tray.RowCount;
                    if (col % 2 == 0)
                        row = index % _tray.RowCount;
                    else
                        row = _tray.ColCount - index % _tray.RowCount - 1;
                    break;
            }
            return new Point(row, col);
        }
        public int GetCellIndex(int row, int col)
        {
            int index = 0;
            switch (_tray.Direction)
            {
                case TrayDir.XZ:
                    index = row * _tray.ColCount + col;
                    break;
                case TrayDir.XS:
                    if(row % 2 == 0)
                    {
                        index = _tray.ColCount * row + col;
                    }
                    else
                    {
                        index = _tray.ColCount * row + (_tray.ColCount - col - 1);
                    }
                    row = index / _tray.ColCount;
                    break;
                case TrayDir.YZ:
                    index = col * _tray.RowCount + row;
                    break;
                case TrayDir.YS:
                    if(col % 2 == 0)
                    {
                        index = col * _tray.RowCount + row;
                    }
                    else
                    {
                        index = col * _tray.RowCount + (row + 1 - _tray.ColCount);
                    }
                    break;
            }
            return index;
        }
        public Cell GetCell(int row, int col)
        {
            return _cells[row, col];
        }
        public Cell GetCell(int index)
        {
            Point p = GetCellRowCol(index);
            return GetCell(p.X, p.Y);
        }
        public int GetFirstIndex(CellStatus status)
        {
            for (int i = 0; i < _tray.RowCount * _tray.ColCount; i++)
            {
                if (GetCell(i).Status == status)
                    return i;
            }
            return -1;
        }
        public void SetCellStatus(int index, CellStatus status)
        {
            Point p = GetCellRowCol(index);
            GetCell(p.X, p.Y).Status = status;
            if (isSimulate)
            {
                if (GetCell(p.X, p.Y).Status == CellStatus.NoProduct && _tray.CellShapes != null)
                {
                    Rectangle1NB rect = _tray.CellShapes.Find(r => r.Alias.Equals(index.ToString()));
                    if (rect != null)
                        rect.PaintColor = CellColor.Instance.Status2Color[status];
                }
                else if (GetCell(p.X, p.Y).Result == TestResult.NoTest && _tray.CellShapes != null)
                {
                    Rectangle1NB rect = _tray.CellShapes.Find(r => r.Alias.Equals(index.ToString()));
                    if (rect != null)
                        rect.PaintColor = CellColor.Instance.Status2Color[status];
                }
            }
        }
        public void SetCellResult(int index, TestResult result)
        {
            Point p = GetCellRowCol(index);
            GetCell(p.X, p.Y).Result = result;
            if (isSimulate)
            {
                if (GetCell(p.X, p.Y).Result != TestResult.NoTest && _tray.CellShapes != null)
                {
                    Rectangle1NB rect = _tray.CellShapes.Find(r => r.Alias.Equals(index.ToString()));
                    if (rect != null)
                        rect.PaintColor = CellColor.Instance.Result2Color[result];
                }
                else
                {

                }
            }
        }
        public void SetCellPreNG(int index, bool preNG)
        {
            Point p = GetCellRowCol(index);
            GetCell(p.X, p.Y).IsPreNG = preNG;
        }
        public void SetCellTesters(int index, List<int> testers)
        {
            Point p = GetCellRowCol(index);
            GetCell(p.X, p.Y).TestedTesters = new List<int>();
            for (int i = 0; i < testers.Count; i++)
            {
                GetCell(p.X, p.Y).TestedTesters.Add(testers[i]);
            }
        }
        public void SetCellBarcode(int index, string barcode)
        {
            Point p = GetCellRowCol(index);
            GetCell(p.X, p.Y).SN = barcode;
            if (isSimulate)
            {
                if (GetCell(p.X, p.Y).Status == CellStatus.HasProduct &&
                    _tray.CellShapes != null &&
                    !string.IsNullOrEmpty(barcode) &&
                    barcode.Equals("NG"))
                {
                    Rectangle1NB rect = _tray.CellShapes.Find(r => r.Alias.Equals(index.ToString()));
                    if (rect != null)
                        rect.PaintColor = "orange";
                }
            }
        }
        public List<PointF> GetMaxMinXY(int nozzleIndex)
        {
            float maxX = 0, maxY = 0;
            for (int i = 0; i < _tray.RowCount; i++)
            {
                for (int j = 0; j < _tray.ColCount; j++)
                {
                    if (GetPoint(i, j, nozzleIndex).X > maxX)
                    {
                        maxX = GetPoint(i, j, nozzleIndex).X;
                    }
                    if (GetPoint(i, j, nozzleIndex).Y > maxY)
                    {
                        maxY = GetPoint(i, j, nozzleIndex).Y;
                    }
                }
            }
            float minX = 0, minY = 0;
            for (int i = 0; i < _tray.RowCount; i++)
            {
                for (int j = 0; j < _tray.ColCount; j++)
                {
                    if (GetPoint(i, j, nozzleIndex).X < minX)
                    {
                        minX = GetPoint(i, j, nozzleIndex).X;
                    }
                    if (GetPoint(i, j, nozzleIndex).Y < minY)
                    {
                        minY = GetPoint(i, j, nozzleIndex).Y;
                    }
                }
            }
            return new List<PointF>() { new PointF(maxX, maxY), new PointF(minX, minY) };
        }

        public IEnumerator<Cell> GetEnumerator()
        {
            for (int i = 0; i < _tray.RowCount * _tray.ColCount; i++)
            {
                yield return GetCell(i);
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}
